#include <sys/types.h>
#include <sys/stat.h> 
#include <fcntl.h>
#include <sys/mman.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stat_manager.h>
#include <Singleton.h>
#include <global.h>
#include <log.h>
#include <string.h>
#include <errno.h>
#include <memcheck.h>
#include "tm_task.h"

HTTP_SVR_NS_BEGIN

CStatManager::CStatManager (void) :
_cycle (1),
_web_req_u32 (NULL),
_web_rsp_u32 (NULL),
_helper_req_u32 (NULL),
_helper_rsp_u32 (NULL),
threadid (0)
{
    pthread_mutex_init(&wakeLock, NULL);    
}

CStatManager::~CStatManager (void)
{
    release();    
}

void *CStatManager::__threadentry(void *p)
{
    CStatManager *my = (CStatManager *)p;
    my->run ();
    return NULL;
}

CStatManager* CStatManager::Instance (void)
{
    return CSingleton <CStatManager>::Instance (); 
}

void CStatManager::Destroy (void)
{
    return CSingleton <CStatManager>::Destroy (); 
}

void* CStatManager::get_stat_mem (const char* filename, int size)
{    
    int fd = ::open(filename, O_RDWR|O_CREAT, 0666);
    void *map = NULL;

    if(fd >= 0)
    {
        if(size > 0){
            if(ftruncate(fd, size)){};
		}
        else
            size = lseek(fd, 0L, SEEK_END);

        if(size > 0)
            map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        ::close(fd);
    } else if(size > 0) {
        map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0);
    }

    if(map == MAP_FAILED)
    {
        map = NULL;
    }

    return map;
}

int CStatManager::init_cgi_stat_item (void)
{
    std::pair<TStatItemMap::iterator, bool> result;
    _itemmap.clear ();

    //create stat head info
    _stathead = (TStatHeadInfo*) get_stat_mem ("stat.dat", sizeof(TStatHeadInfo) + MAX_CGI_COUNT * sizeof(TCGIStatData));

    if (NULL == _stathead)
    {
        log_boot ("create TStatHeadInfo object failed, msg[%s]", strerror(errno));
        return -1;
    }

    memset (_stathead, 0x00, sizeof(TStatHeadInfo) + MAX_CGI_COUNT * sizeof(TCGIStatData));

    TCGIOperInfo* cgi_oper_info = NULL;
    
    for (int i = 0; i < MAX_CGI_COUNT; ++i)
    {
        //set cgi index
        //TCGIStatData* cgi_stat_data = (TCGIStatData*)(_stathead + sizeof(TStatHeadInfo));
        TCGIStatData* cgi_stat_data = (TCGIStatData*)(_stathead->_cgi_data);
        cgi_stat_data[i]._cgi_index = i;

        //set stat oper info
        cgi_oper_info = (TCGIOperInfo*) MALLOC (sizeof(TCGIOperInfo));

        if (NULL == cgi_oper_info)
        {
            log_boot ("malloc memory failed, size[%d]", (int)sizeof(TCGIOperInfo));

            return -1;
        }

        memset (cgi_oper_info, 0x00, sizeof(TCGIOperInfo));
        NEW (CStatItemU32(&(cgi_stat_data[i]._cgi_req_count)), cgi_oper_info->_cgi_req);
        NEW (CStatItemU32(&(cgi_stat_data[i]._cgi_rsp_count)), cgi_oper_info->_cgi_rsp);
        cgi_oper_info->_cgi_stat_data = &cgi_stat_data[i];

        if (NULL == cgi_oper_info->_cgi_req || NULL == cgi_oper_info->_cgi_rsp)
        {
            log_boot ("create cgi oper object error");

            return -1;
        }

        result = _itemmap.insert (TStatItemMap::value_type(i, cgi_oper_info));

        if (!result.second)
        {
            log_boot ("insert a TCGIOperInfo object failed.");

            return -1;
        }
    }

    return 0;
}

int CStatManager::open (void)
{
    _cycle = TGlobal::_cleancycle;

    //init stat item map
    if (init_cgi_stat_item() < 0)
        return -1;

   //create web request stat object.
    static CStatItemU32 web_req_u32 (&(_stathead->_web_req_count));
    _web_req_u32 = &web_req_u32;

    //create web response stat object.
    static CStatItemU32 web_rsp_u32 (&(_stathead->_web_rsp_count));
    _web_rsp_u32 = &web_rsp_u32;

    //create web response stat object.
    static CStatItemU32 helper_req_u32 (&(_stathead->_helper_req_count));
    _helper_req_u32 = &helper_req_u32;

    //create web response stat object.
    static CStatItemU32 helper_rsp_u32 (&(_stathead->_helper_rsp_count));
    _helper_rsp_u32 = &helper_rsp_u32;

    CScopedLock guard (_mutex);
    
    /*
    if(pthread_mutex_trylock(&wakeLock) == 0)
    {
        int ret = pthread_create(&threadid, 0, __threadentry, (void *)this);

        if(ret != 0)
        {
            log_boot ("create stat. thread failed, %s", strerror(errno));

            errno = ret;
            return -1;
        }
    }
    */

    return 0;
}

int CStatManager::release(void)
{
    CScopedLock guard (_mutex);

    if(pthread_mutex_trylock(&wakeLock) == 0)
    {
        pthread_mutex_unlock(&wakeLock);

        return 0;
    }

    pthread_mutex_unlock(&wakeLock);
    int ret = pthread_join(threadid, 0);

    if (0 == ret)
    {
        for (TStatItemMap::const_iterator i = _itemmap.begin(); i != _itemmap.end(); i++)
        {
            DELETE ((*i).second->_cgi_req);
            DELETE ((*i).second->_cgi_rsp);
            FREE((*i).second);
        }

        _itemmap.clear ();

        return 0;
    }

    log_boot ("cannot stop thread[stat]");

    return ret; 
}

static void BlockAllSignals(void)
{
    sigset_t sset;
    sigfillset(&sset);
    sigdelset(&sset, SIGSEGV);
    sigdelset(&sset, SIGBUS);
    sigdelset(&sset, SIGABRT);
    sigdelset(&sset, SIGILL);
    sigdelset(&sset, SIGCHLD);
    sigdelset(&sset, SIGFPE);
    pthread_sigmask(SIG_SETMASK, &sset, &sset);
}

int CStatManager::clear (void)
{
    _web_req_u32->clear ();
    _web_rsp_u32->clear ();

    _helper_req_u32->clear ();
    _helper_rsp_u32->clear ();

    for (TStatItemMap::const_iterator i = _itemmap.begin(); i != _itemmap.end(); i++)
    {
        ((*i).second)->_cgi_req->clear ();
        ((*i).second)->_cgi_rsp->clear ();
    }

    return 0; 
}

int CStatManager::run (void)
{
    BlockAllSignals();

    int cleanup     = _cycle <= 0 ? 0 : 1;
    int clean_cycle = _cycle <= 0 ? 60 : _cycle;
    time_t next     = 0;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    next = (tv.tv_sec / clean_cycle) * clean_cycle + clean_cycle;
    struct timespec ts;
    ts.tv_sec = next;
    ts.tv_nsec = 0;

    while (pthread_mutex_timedlock(&wakeLock, &ts) != 0)
    {
        gettimeofday(&tv, NULL);
        if(tv.tv_sec >= next)
        {
            if (cleanup) clear ();
            gettimeofday(&tv, NULL);
            next = (tv.tv_sec / clean_cycle) * clean_cycle + clean_cycle;
        }
        ts.tv_sec = next;
        ts.tv_nsec = 0;
    }

    pthread_mutex_unlock(&wakeLock);

    return 0;
}

TItemU32 CStatManager::add_req_by_id (int item_id, TItemU32 value)
{
    CStatItemU32* stat_item_u32 = NULL;
    TStatItemMap::const_iterator it = _itemmap.find (item_id);

    if (_itemmap.end() != it)
    {
        stat_item_u32 = ((*it).second)->_cgi_req;
        *stat_item_u32 += value;
    }    

    return *stat_item_u32;
}

TItemU32 CStatManager::sub_req_by_id (int item_id, TItemU32 value)
{
    CStatItemU32* stat_item_u32 = NULL;
    TStatItemMap::const_iterator it = _itemmap.find (item_id);

    if (_itemmap.end() != it)
    {
        stat_item_u32 = ((*it).second)->_cgi_req;
        *stat_item_u32 -= value;
    }    

    return *stat_item_u32;
}

TItemU32 CStatManager::add_rsp_by_id (int item_id, TItemU32 value)
{
    CStatItemU32* stat_item_u32 = NULL;

    TStatItemMap::const_iterator it = _itemmap.find (item_id);

    if (_itemmap.end() != it)
    {
        stat_item_u32 = ((*it).second)->_cgi_rsp;
        *stat_item_u32 += value;
    }

    return *stat_item_u32;

}

TItemU32 CStatManager::sub_rsp_by_id (int item_id, TItemU32 value)
{
    CStatItemU32* stat_item_u32 = NULL;

    TStatItemMap::const_iterator it = _itemmap.find (item_id);

    if (_itemmap.end() != it)
    {
        stat_item_u32 = ((*it).second)->_cgi_rsp;
        *stat_item_u32 -= value;
    }

    return *stat_item_u32;

}

HTTP_SVR_NS_END
